4장. 파이썬으로 숫자 다루기 (NumPy·PyTorch)
출처: 『AI 엔지니어링 선수지식』(youtubedu 자체 제작 학습노트) | 입문판 재구성 — 노트가 1차 소스(PDF 원본 없음)
코드는 분위기만 —
np·torch·import같은 말은 몰라도 됩니다. 표의 '비유'와 '위험'만 봐도 충분해요.
이 장은 도구 이야기다.
앞 장들에서 숫자·방향·확률을 손으로 풀어 봤다.
이제 그 계산을 컴퓨터에게 시키는 두 도구를 만난다.
하나는 NumPy, 하나는 PyTorch다.
둘 다 하는 일은 똑같다.
숫자 묶음(벡터·행렬)을 빠르게 다룬다.
겁먹지 말자.
새 개념은 거의 없고, 이미 아는 것을 더 빨리 하는 방법일 뿐이다.
0. 이 장의 새 단어 (0장에 없는 것만 3개)
0장 용어집은 그대로 쓴다.
여기서는 이 장에만 새로 나오는 도구 이름 3개만 푼다.
NumPy 배열 (ndarray)
한 문장 뜻 — 숫자를 격자로 담아 한 번에 계산하는 빠른 통.
일상비유 — 엑셀 시트의 코드 버전. 셀에 숫자를 채우고 버튼 한 번으로 전부 계산하는 그 느낌.
한 줄 예 —
# 숫자 묶음을 빠른 통에 담는다
a = np.array([1, 2, 3])
브로드캐스팅 (broadcasting)
한 문장 뜻 — 모양이 다른 두 묶음을 더하거나 곱할 때, 작은 쪽을 자동으로 늘려 맞추는 규칙.
일상비유 — 반 전원에게 +5점. 5라는 숫자 하나가 학생 수만큼 저절로 복사되어 더해진다.
한 줄 예 —
# 점수 3개에 5를 한 번에 더한다 (5가 자동 복사됨)
scores = np.array([80, 90, 70]) + 5 # [85 95 75]
PyTorch 텐서 / autograd
한 문장 뜻 — NumPy 배열에 GPU 가속과 자동 미분을 얹은 형(텐서). autograd는 그 자동 미분 기능.
일상비유 — NumPy가 보통 계산기라면, 텐서는 GPU로 빨라지고 미분까지 알아서 해 주는 고급 계산기.
한 줄 예 —
# requires_grad=True 를 붙이면 미분을 알아서 계산해 준다
x = torch.tensor(2.0, requires_grad=True)
이런 적 있죠?
학생 30명의 시험 점수가 있다.
전원에게 5점씩 더해 주고 싶다.
파이썬 기본 리스트로 하면 반복문을 돌려야 한다.
# 기본 리스트 — 한 명씩 돌면서 더한다
new_scores = [s + 5 for s in scores]
한 명, 또 한 명, 30번을 돈다.
30명이면 괜찮다.
그런데 1000만 명이면?
수백만 칸짜리 임베딩이면?
반복문이 너무 느려진다.
여기서 NumPy가 등판한다.
NumPy는 반복문 없이 묶음 전체를 한 줄로 계산한다.
# NumPy — 묶음 전체를 한 줄로
new_scores = np.array(scores) + 5
같은 일을 수십 배 빠르게 한다.
그게 이 장의 핵심이다.
한 문장 정의 — NumPy와 PyTorch는 숫자 묶음을 반복문 없이 한 번에, 그리고 빠르게 계산해 주는 AI의 손과 발이다.
이 장에서 딱 3가지만 (TL;DR)
- NumPy 배열 — 숫자를 격자에 담아 반복문 없이 한 줄로 계산한다.
- 브로드캐스팅 — 모양이 달라도 작은 쪽을 자동으로 늘려 맞춰 더한다.
- PyTorch 텐서·autograd — NumPy에 GPU와 자동 미분을 얹어, 미분을 손으로 안 짜도 된다.
이 셋만 손에 잡으면 AI 코드가 술술 읽힌다.
개념 1 — NumPy 배열 (숫자 격자)
막히는 장면
벡터 100만 개의 평균을 구해야 한다.
기본 리스트로는 for 문을 돌려야 한다.
칸이 많을수록 느려진다.
NumPy는 같은 일을 통째로, 빠르게 한다.
일상비유 — 엑셀의 코드 버전.
엑셀에서 한 열에 숫자를 쭉 채운다.
맨 아래에 =평균() 한 줄을 넣으면 끝이다.
한 칸씩 더하지 않는다.
NumPy 배열(ndarray)이 바로 그 엑셀을 코드로 옮긴 것이다.
숫자를 격자에 담고, 반복문 없이 한 줄로 연산한다.
| 비유 | 코드 | 위험 |
|---|---|---|
| 엑셀 시트 | a = np.array([[1,2,3],[4,5,6]]) |
기본 리스트보다 빠르지만, 일반 list와 섞어 쓰면 느려짐 |
=평균() 한 줄 |
a.mean(axis=0) |
axis(행/열 방향)를 헷갈리면 엉뚱한 평균이 나옴 |
| 조건 자동 추출 | a[a > 3] |
모양(shape)이 안 맞으면 계산이 막힘 |
한 문장 정의 — NumPy 배열(ndarray)은 숫자를 격자에 담아 반복문 없이 한 줄로 빠르게 계산하는 통이다.
단순 규칙 — 숫자가 여러 개면 일단 np.array에 담는다. 그게 시작이다.
예시 폭격 1 — NumPy 배열
예시 ① 완성 예 (worked) — 보고 따라 읽기
import numpy as np
a = np.array([[1, 2, 3], [4, 5, 6]]) # 2행 3열 격자
print(a.shape) # (2, 3) → 2행 3열이라는 뜻
print(a.mean(axis=0)) # [2.5 3.5 4.5] → 열별 평균
print(a[a > 3]) # [4 5 6] → 3보다 큰 칸만 골라냄
shape는 격자의 모양이다.
mean(axis=0)은 위아래(열) 방향 평균이다.
a[a > 3]은 조건에 맞는 칸만 한 줄로 뽑는다.
반복문이 한 줄도 없다.
예시 ② 일상 장면 — before / after
같은 일을 두 가지 방식으로 해 보자.
# before — 기본 리스트, 반복문 필요
nums = [1, 2, 3]
doubled = [x * 2 for x in nums] # [2, 4, 6]
# after — NumPy, 한 줄
nums = np.array([1, 2, 3])
doubled = nums * 2 # [2 4 6]
결과는 똑같다.
코드는 더 짧고, 데이터가 커질수록 더 빠르다.
예시 ③ 부분 완성 — 빈칸 채우기
학생 점수 격자에서 "각 과목(열)의 평균"을 구하고 싶다.
빈칸 ____를 채워 보자.
scores = np.array([[90, 85, 100], [70, 95, 80]])
# 힌트: 위아래(열) 방향 평균은 axis=0
col_avg = scores.____(axis=0)
정답은 mean이다.
scores.mean(axis=0) → [80. 90. 90.].
예시 ④ 독립 적용 — 스스로
배열 np.array([5, 1, 8, 3])에서 4보다 큰 값만 뽑는 한 줄을 떠올려 보자.
답: arr[arr > 4] → [5 8].
조건을 대괄호 안에 넣으면 자동으로 필터된다.
미니 시나리오 — "이럴 때 이렇게"
이미지 한 장을 다룰 때.
이미지는 결국 픽셀 숫자의 큰 격자다.
밝기를 전부 절반으로 줄이려면?
기본 리스트로는 픽셀 수만큼 반복문을 돈다.
NumPy로는 image = image * 0.5 한 줄이다.
이래서 데이터 전처리·통계·이미지 처리의 기본기가 전부 NumPy다.
실무 메모 — Pandas, scikit-learn, PyTorch가 전부 NumPy 위에 지어졌다. 임베딩·확률 묶음도 NumPy로 다룬다. 모든 묶음 연산의 출발점이다.
더 깊이 (지금 몰라도 됨) — NumPy Absolute Beginners
개념 2 — 브로드캐스팅 (자동으로 모양 맞추기)
막히는 장면
학생 30명 점수에 전원 +5점을 하고 싶다.
머리로는 "5를 30번 복사해서 각자에게 더한다"고 생각하기 쉽다.
그런데 NumPy에서는 5를 30개로 만들 필요가 없다.
그냥 + 5 하면 알아서 늘려 맞춰 더한다.
이 "자동으로 모양 맞추기"가 브로드캐스팅이다.
일상비유 — 양념 한 스푼씩.
찌개 한 솥에 소금을 넣을 때.
국자마다 따로 소금을 넣지 않는다.
한 번 넣으면 솥 전체에 퍼진다.
브로드캐스팅도 똑같다.
숫자 하나(5)가 묶음 전체에 알아서 퍼져 더해진다.
| 비유 | 코드 | 위험 |
|---|---|---|
| 전원 +5점 | np.array([80,90,70]) + 5 |
모양이 아예 안 맞으면(예: 3칸 + 4칸) 오류가 남 |
| 솥에 소금 퍼지기 | M - M.mean(axis=0) |
axis를 잘못 잡으면 엉뚱한 방향으로 퍼짐 |
| 복사 안 해도 됨 | 작은 쪽이 자동으로 늘어남 | 늘어나는 규칙을 모르면 결과 모양을 예측 못 함 |
한 문장 정의 — 브로드캐스팅은 모양이 다른 두 묶음을 연산할 때 작은 쪽을 자동으로 늘려 맞춰 주는 규칙이다.
단순 규칙 — 숫자 하나를 묶음 전체에 더하거나 곱하고 싶으면, 그냥 더하거나 곱하면 된다. 늘리는 건 NumPy가 한다.
예시 폭격 2 — 브로드캐스팅
예시 ① 완성 예 (worked) — 전원 +5
import numpy as np
scores = np.array([[80], [90], [70]]) # 학생 3명, 세로 묶음
print(scores + 5)
# [[85]
# [95]
# [75]]
5라는 숫자 하나가 학생 수만큼 자동으로 퍼졌다.
5를 손으로 복사하지 않았다.
예시 ② 핵심 활용 — 평균 빼기(정규화 맛보기)
M = np.array([[1, 2, 3], [4, 5, 6]])
print(M - M.mean(axis=0))
# 각 열에서 그 열의 평균을 자동으로 뺀다
M.mean(axis=0)은 열별 평균(작은 묶음)이다.
그게 M(큰 묶음)의 모든 행에 자동으로 퍼져서 빼진다.
이게 정규화의 기본 동작이다.
예시 ③ 부분 완성 — 빈칸 채우기
온도 묶음 [20, 25, 30]에서 전부 10을 빼고 싶다.
temps = np.array([20, 25, 30])
result = temps ____ 10 # 빈칸을 채워라
정답은 -다.
temps - 10 → [10 15 20].
10 하나가 세 칸에 자동으로 퍼져 빠진다.
예시 ④ 독립 적용 — before / after
"가격 묶음 전체에 1.1을 곱해 10% 인상"을 떠올려 보자.
# before — 반복문
prices = [100, 200, 300]
raised = [p * 1.1 for p in prices]
# after — 브로드캐스팅
prices = np.array([100, 200, 300])
raised = prices * 1.1 # [110. 220. 330.]
1.1 하나가 모든 칸에 곱해진다.
미니 시나리오 — "이럴 때 이렇게"
문장 여러 개에 같은 가중치를 적용해야 할 때.
문장 100개를 한 격자(배치)에 쌓는다.
가중치 묶음 하나를 그 격자에 곱한다.
브로드캐스팅이 가중치를 100개 문장 전부에 자동으로 퍼뜨린다.
반복문 없이 한 줄로 끝난다.
실무 메모 — 정규화(평균 빼고 표준편차 나누기)가 전부 브로드캐스팅이라, 코드가 짧고 빨라진다.
한 걸음 더 ▸ (지금 몰라도 됨) — 모양을 어떻게 늘릴지에는 정해진 규칙이 있다. 칸 수가 1이거나 같으면 맞춰지고, 둘 다 아니면 오류가 난다. 지금은 "작은 쪽이 자동으로 퍼진다"만 기억하면 충분하다.
개념 3 — PyTorch 텐서·autograd (미분을 자동으로)
막히는 장면
앞에서 미분을 손으로 구했다.
y = x³의 미분은 3x²다.
그런데 AI 모델은 다이얼(가중치)이 수천억 개다.
수천억 개를 손으로 미분할 수는 없다.
여기서 PyTorch가 등판한다.
PyTorch에게 x만 넘기면, 미분을 알아서 계산해 준다.
이 자동 미분 기능이 autograd다.
일상비유 — NumPy의 형.
NumPy 배열이 보통 계산기라고 하자.
텐서는 그 계산기의 업그레이드 버전이다.
두 가지가 더 붙는다.
하나는 GPU 가속(아주 빠름), 하나는 자동 미분(autograd).
겉모습은 NumPy 배열과 거의 같다.
| 비유 | 코드 | 위험 |
|---|---|---|
| 업그레이드된 계산기 | torch.tensor(2.0) |
NumPy와 비슷해 보여도 별도 도구라, 둘을 섞을 때 변환이 필요 |
| 미분 켜는 스위치 | requires_grad=True |
이 스위치를 안 켜면 미분(grad)이 안 나옴 |
| 미분 자동 계산 | y.backward() 뒤 x.grad |
backward()를 안 부르면 x.grad가 비어 있음 |
한 문장 정의 — PyTorch 텐서는 GPU 가속과 자동 미분(autograd)을 얹은 NumPy 배열이며, autograd 덕에 미분을 손으로 짤 필요가 없다.
단순 규칙 — 미분이 필요하면 텐서에 requires_grad=True를 켜고, backward()를 부른 뒤 .grad를 읽는다. 그 세 단계가 전부다.
예시 폭격 3 — PyTorch 텐서·autograd
예시 ① 완성 예 (worked) — 손 미분 vs 자동 미분
import torch
x = torch.tensor(2.0, requires_grad=True) # 미분 스위치 ON
y = x ** 3 # y = x³
y.backward() # 미분을 자동 계산
print(x.grad) # tensor(12.)
손으로 풀면 x³의 미분은 3x²다.
x = 2를 넣으면 3 × 2² = 12다.
PyTorch가 똑같이 12를 자동으로 내놓았다.
손 미분과 답이 같다.
예시 ② before / after — 손 vs PyTorch
# before — 손으로 미분 공식을 직접 짠다
def slope(x):
return 3 * x ** 2 # x³ 의 미분 공식을 사람이 안다고 가정
print(slope(2.0)) # 12.0
# after — PyTorch가 공식을 몰라도 자동으로
x = torch.tensor(2.0, requires_grad=True)
(x ** 3).backward()
print(x.grad) # tensor(12.)
before는 미분 공식을 사람이 알아야 한다.
after는 식만 적으면 PyTorch가 알아서 미분한다.
복잡한 식일수록 after의 이득이 커진다.
예시 ③ 부분 완성 — 빈칸 채우기
y = x²의 미분을 자동으로 구하고 싶다.
x = 3일 때 기울기는 2x = 6이어야 한다.
x = torch.tensor(3.0, requires_grad=True)
y = x ** 2
y.________() # 빈칸: 미분을 계산하는 메서드
print(x.grad) # tensor(6.)
정답은 backward다.
backward()를 불러야 x.grad에 값이 채워진다.
예시 ④ 독립 적용 — 스위치를 까먹으면?
x = torch.tensor(2.0) # requires_grad 를 안 켰다
y = x ** 3
# y.backward() ← 여기서 오류가 난다
requires_grad=True를 빠뜨리면 미분이 안 된다.
위험 표의 "스위치를 안 켜면 grad가 안 나옴"이 바로 이 상황이다.
스위치부터 켜는 습관이 안전판이다.
미니 시나리오 — "이럴 때 이렇게"
거대 모델이 틀린 답을 냈다.
어느 다이얼을 어느 쪽으로 돌릴지 알아야 한다.
다이얼이 수천억 개다.
손 미분은 불가능하다.
PyTorch는 모델 전체에 backward() 한 번을 불러, 모든 다이얼의 그래디언트를 자동으로 채운다.
그래서 모든 딥러닝 모델이 PyTorch(또는 비슷한 도구)로 작성된다.
실무 메모 — 모든 LLM이 텐서로 구현된다. autograd가 미분과 역전파를 자동화해, 사람이 그 계산을 손으로 짤 필요가 없다.
더 깊이 (지금 몰라도 됨) — PyTorch Learn the Basics
정리
세 줄로 끝낸다.
NumPy 배열은 숫자를 격자에 담아 반복문 없이 한 줄로 빠르게 계산한다.
브로드캐스팅은 모양이 달라도 작은 쪽을 자동으로 늘려 맞춰 준다.
PyTorch 텐서는 NumPy에 GPU와 자동 미분(autograd)을 얹어, 미분을 손으로 안 짜게 해 준다.
| 도구 | 한마디 |
|---|---|
| NumPy 배열 | 숫자 묶음 빠르게 다루기 (벡터·행렬) |
| 브로드캐스팅 | 모양 자동 맞춤 (정규화·배치) |
| PyTorch 텐서 | GPU 가속 배열 |
| autograd | 미분·역전파 자동화 |
한 문장으로, NumPy로 숫자를 빠르게, PyTorch로 GPU와 자동 미분까지.
이 둘이 AI 코드의 손과 발이다.
다음 장 예고 — 다음 장에서는 지금까지 모은 조각(숫자·방향·확률·도구)이 실제 AI 안에서 어떻게 한 흐름으로 이어지는지 본다. 지금은 이 장 도구 3개만 머리에 있으면 충분하다.
클릭하거나 Space를 눌러 뒤집기